using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Principal;
using System.Text;

namespace IppServer.Host.Configuration
{
    /// <summary>
    /// Windows防火墙规则管理器
    /// </summary>
    internal static class FirewallManager
    {
        private const string RuleNamePrefix = "IPP Server - Port ";
        private const string TcpRuleName = "IPP Server - TCP Ports";

        /// <summary>
        /// 检查当前是否以管理员权限运行
        /// </summary>
        public static bool IsRunningAsAdministrator()
        {
            try
            {
                var identity = WindowsIdentity.GetCurrent();
                var principal = new WindowsPrincipal(identity);
                return principal.IsInRole(WindowsBuiltInRole.Administrator);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 生成防火墙规则名称
        /// </summary>
        private static string GetRuleName(int port)
        {
            return $"{RuleNamePrefix}{port}";
        }

        /// <summary>
        /// 添加防火墙入站规则
        /// </summary>
        /// <param name="port">端口号</param>
        /// <param name="logCallback">日志回调</param>
        /// <returns>是否成功</returns>
        public static bool AddFirewallRule(int port, Action<string> logCallback = null)
        {
            if (port < 1 || port > 65535)
            {
                logCallback?.Invoke($"无效的端口号：{port}");
                return false;
            }

            var ruleName = GetRuleName(port);

            try
            {
                // 检查规则是否已存在
                if (FirewallRuleExists(ruleName))
                {
                    logCallback?.Invoke($"防火墙规则 '{ruleName}' 已存在，跳过添加。");
                    return true;
                }

                // 检查是否以管理员权限运行
                if (!IsRunningAsAdministrator())
                {
                    logCallback?.Invoke($"警告：未以管理员权限运行，无法自动添加防火墙规则。请手动添加端口 {port} 的入站规则。");
                    return false;
                }

                // 使用 netsh 命令添加防火墙规则（兼容 Windows 7/10/11）
                var result = ExecutePowerShellFirewallCommand(ruleName, port, logCallback);

                if (result)
                {
                    logCallback?.Invoke($"已添加防火墙入站规则：'{ruleName}' (端口 {port})");
                }
                else
                {
                    logCallback?.Invoke($"添加防火墙规则失败：端口 {port}。请手动在Windows防火墙中开放此端口。");
                }

                return result;
            }
            catch (Exception ex)
            {
                logCallback?.Invoke($"添加防火墙规则时发生错误：{ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 删除防火墙入站规则
        /// </summary>
        /// <param name="port">端口号</param>
        /// <param name="logCallback">日志回调</param>
        /// <returns>是否成功</returns>
        public static bool RemoveFirewallRule(int port, Action<string> logCallback = null)
        {
            if (port < 1 || port > 65535)
            {
                return false;
            }

            var ruleName = GetRuleName(port);

            try
            {
                // 检查规则是否存在
                if (!FirewallRuleExists(ruleName))
                {
                    logCallback?.Invoke($"防火墙规则 '{ruleName}' 不存在，跳过删除。");
                    return true;
                }

                // 检查是否以管理员权限运行
                if (!IsRunningAsAdministrator())
                {
                    logCallback?.Invoke($"警告：未以管理员权限运行，无法删除防火墙规则。");
                    return false;
                }

                // 使用 netsh 命令删除防火墙规则（兼容 Windows 7/10/11）
                var result = ExecutePowerShellFirewallDeleteCommand(ruleName, logCallback);

                if (result)
                {
                    logCallback?.Invoke($"已删除防火墙规则：'{ruleName}' (端口 {port})");
                }

                return result;
            }
            catch (Exception ex)
            {
                logCallback?.Invoke($"删除防火墙规则时发生错误：{ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 检查防火墙规则是否存在
        /// </summary>
        private static bool FirewallRuleExists(string ruleName)
        {
            try
            {
                if (!IsRunningAsAdministrator())
                {
                    return false;
                }

                // 使用 netsh 命令检查防火墙规则是否存在（兼容 Windows 7/10/11）
                return FirewallRuleExistsByPowerShell(ruleName);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 使用 netsh 命令添加防火墙规则（兼容 Windows 7/10/11）
        /// </summary>
        private static bool ExecutePowerShellFirewallCommand(string ruleName, int port, Action<string> logCallback = null)
        {
            try
            {
                // 使用 netsh 命令，兼容性更好，支持 Windows 7/10/11
                var command = $"advfirewall firewall add rule name=\"{ruleName}\" dir=in action=allow protocol=TCP localport={port}";
                var result = ExecuteNetshCommand(command, logCallback);

                if (result)
                {
                    logCallback?.Invoke($"已添加防火墙入站规则：'{ruleName}' (端口 {port})");
                }
                else
                {
                    logCallback?.Invoke($"添加防火墙规则失败：端口 {port}。请手动在Windows防火墙中开放此端口。");
                }

                return result;
            }
            catch (Exception ex)
            {
                logCallback?.Invoke($"添加防火墙规则时发生错误：{ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 使用 netsh 命令删除防火墙规则（兼容 Windows 7/10/11）
        /// </summary>
        private static bool ExecutePowerShellFirewallDeleteCommand(string ruleName, Action<string> logCallback = null)
        {
            try
            {
                // 使用 netsh 命令删除防火墙规则，兼容性更好
                var command = $"advfirewall firewall delete rule name=\"{ruleName}\"";
                var result = ExecuteNetshCommand(command, logCallback);

                if (result)
                {
                    logCallback?.Invoke($"已删除防火墙规则：'{ruleName}'");
                }

                return result;
            }
            catch (Exception ex)
            {
                logCallback?.Invoke($"删除防火墙规则时发生错误：{ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 使用 netsh 命令检查防火墙规则是否存在（兼容 Windows 7/10/11）
        /// </summary>
        private static bool FirewallRuleExistsByPowerShell(string ruleName)
        {
            try
            {
                if (!IsRunningAsAdministrator())
                {
                    return false;
                }

                var command = $"advfirewall firewall show rule name=\"{ruleName}\"";
                var processInfo = new ProcessStartInfo
                {
                    FileName = "netsh",
                    Arguments = command,
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = true,
                    StandardOutputEncoding = Encoding.UTF8
                };

                using (var process = Process.Start(processInfo))
                {
                    if (process == null)
                    {
                        return false;
                    }

                    var output = process.StandardOutput.ReadToEnd();
                    process.WaitForExit(3000);

                    // 如果规则存在，netsh 会输出规则信息
                    // 如果不存在，通常会输出 "未找到规则" 或类似的错误信息
                    return !string.IsNullOrWhiteSpace(output) && 
                           !output.Contains("未找到") && 
                           !output.Contains("not found") &&
                           output.Contains(ruleName);
                }
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 执行 netsh 命令
        /// </summary>
        private static bool ExecuteNetshCommand(string arguments, Action<string> logCallback = null)
        {
            try
            {
                var processInfo = new ProcessStartInfo
                {
                    FileName = "netsh",
                    Arguments = arguments,
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = true,
                    StandardOutputEncoding = Encoding.UTF8
                };

                using (var process = Process.Start(processInfo))
                {
                    if (process == null)
                    {
                        logCallback?.Invoke("无法启动 netsh 进程。");
                        return false;
                    }

                    var output = process.StandardOutput.ReadToEnd();
                    var error = process.StandardError.ReadToEnd();
                    process.WaitForExit(5000);

                    if (process.ExitCode == 0)
                    {
                        if (!string.IsNullOrWhiteSpace(output))
                        {
                            logCallback?.Invoke($"防火墙操作输出：{output.Trim()}");
                        }
                        return true;
                    }
                    else
                    {
                        var errorMsg = !string.IsNullOrWhiteSpace(error) ? error : output;
                        logCallback?.Invoke($"防火墙操作失败：{errorMsg.Trim()}");
                        return false;
                    }
                }
            }
            catch (Exception ex)
            {
                logCallback?.Invoke($"执行 netsh 命令时发生错误：{ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 确保防火墙规则存在（如果不存在则添加）
        /// </summary>
        /// <param name="port">端口号</param>
        /// <param name="logCallback">日志回调</param>
        /// <returns>是否成功或已存在</returns>
        public static bool EnsureFirewallRule(int port, Action<string> logCallback = null)
        {
            if (port < 1 || port > 65535)
            {
                logCallback?.Invoke($"无效的端口号：{port}");
                return false;
            }

            var ruleName = GetRuleName(port);

            // 如果规则已存在，直接返回成功
            if (FirewallRuleExists(ruleName))
            {
                logCallback?.Invoke($"防火墙规则 '{ruleName}' 已存在。");
                return true;
            }

            // 否则尝试添加
            return AddFirewallRule(port, logCallback);
        }

        /// <summary>
        /// 切换防火墙规则：删除旧端口规则，添加新端口规则
        /// </summary>
        /// <param name="newPort">新端口号</param>
        /// <param name="oldPort">旧端口号（如果为null或<=0则忽略）</param>
        /// <param name="logCallback">日志回调</param>
        /// <returns>是否成功</returns>
        public static bool SwitchFirewallRule(int newPort, int? oldPort, Action<string> logCallback = null)
        {
            if (newPort < 1 || newPort > 65535)
            {
                logCallback?.Invoke($"无效的新端口号：{newPort}");
                return false;
            }

            var success = true;

            // 如果旧端口有效且与新端口不同，则删除旧端口规则
            if (oldPort.HasValue && oldPort.Value > 0 && oldPort.Value != newPort)
            {
                logCallback?.Invoke($"检测到端口变更：{oldPort.Value} -> {newPort}，正在清理旧端口的防火墙规则...");
                if (RemoveFirewallRule(oldPort.Value, logCallback))
                {
                    logCallback?.Invoke($"已删除旧端口 {oldPort.Value} 的防火墙规则。");
                }
                else
                {
                    // 删除失败不影响新规则的添加
                    logCallback?.Invoke($"删除旧端口 {oldPort.Value} 的防火墙规则失败（可能不存在）。");
                }
            }

            // 添加新端口规则
            if (!EnsureFirewallRule(newPort, logCallback))
            {
                success = false;
            }

            return success;
        }

        /// <summary>
        /// 更新TCP打印机防火墙规则（使用单条规则包含所有TCP端口，使用netsh，兼容Windows 7/10/11）
        /// </summary>
        /// <param name="ports">TCP端口列表</param>
        /// <param name="logCallback">日志回调</param>
        /// <returns>是否成功</returns>
        public static bool UpdateTcpFirewallRule(IEnumerable<int> ports, Action<string> logCallback = null)
        {
            try
            {
                // 检查是否以管理员权限运行
                if (!IsRunningAsAdministrator())
                {
                    logCallback?.Invoke("警告：未以管理员权限运行，无法自动管理TCP防火墙规则。");
                    if (ports != null && ports.Any())
                    {
                        var portList = string.Join(", ", ports);
                        logCallback?.Invoke($"请手动在Windows防火墙中开放TCP端口：{portList}");
                    }
                    return false;
                }

                // 过滤有效的端口
                var validPorts = ports?.Where(p => p >= 1 && p <= 65535).Distinct().OrderBy(p => p).ToList() ?? new List<int>();

                // 如果规则已存在，先删除（因为netsh不支持直接修改端口列表）
                if (FirewallRuleExists(TcpRuleName))
                {
                    logCallback?.Invoke($"检测到TCP防火墙规则已存在，正在更新...");
                    if (!RemoveTcpFirewallRule(logCallback))
                    {
                        logCallback?.Invoke("删除旧TCP防火墙规则失败，将尝试覆盖。");
                    }
                }

                // 如果没有端口，只删除规则即可
                if (validPorts.Count == 0)
                {
                    logCallback?.Invoke("没有TCP端口需要开放，已清理TCP防火墙规则。");
                    return true;
                }

                // 创建包含所有端口的规则（netsh支持逗号分隔的多个端口）
                var portListStr = string.Join(",", validPorts);
                var command = $"advfirewall firewall add rule name=\"{TcpRuleName}\" dir=in action=allow protocol=TCP localport={portListStr}";
                var result = ExecuteNetshCommand(command, logCallback);

                if (result)
                {
                    logCallback?.Invoke($"已更新TCP防火墙规则：'{TcpRuleName}' (端口: {string.Join(", ", validPorts)})");
                }
                else
                {
                    logCallback?.Invoke($"更新TCP防火墙规则失败。请手动在Windows防火墙中开放TCP端口：{string.Join(", ", validPorts)}");
                }

                return result;
            }
            catch (Exception ex)
            {
                logCallback?.Invoke($"更新TCP防火墙规则时发生错误：{ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 删除TCP防火墙规则（删除单条规则，使用netsh，兼容Windows 7/10/11）
        /// </summary>
        /// <param name="logCallback">日志回调</param>
        /// <returns>是否成功</returns>
        public static bool RemoveTcpFirewallRule(Action<string> logCallback = null)
        {
            try
            {
                // 检查规则是否存在
                if (!FirewallRuleExists(TcpRuleName))
                {
                    logCallback?.Invoke($"TCP防火墙规则 '{TcpRuleName}' 不存在，跳过删除。");
                    return true;
                }

                // 检查是否以管理员权限运行
                if (!IsRunningAsAdministrator())
                {
                    logCallback?.Invoke("警告：未以管理员权限运行，无法删除TCP防火墙规则。");
                    return false;
                }

                // 使用 netsh 命令删除防火墙规则（兼容 Windows 7/10/11）
                var command = $"advfirewall firewall delete rule name=\"{TcpRuleName}\"";
                var result = ExecuteNetshCommand(command, logCallback);

                if (result)
                {
                    logCallback?.Invoke($"已删除TCP防火墙规则：'{TcpRuleName}'");
                }

                return result;
            }
            catch (Exception ex)
            {
                logCallback?.Invoke($"删除TCP防火墙规则时发生错误：{ex.Message}");
                return false;
            }
        }
    }
}

